package com.project.poetry.service.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.project.poetry.common.constant.DictConstant;
import com.project.poetry.common.exception.ServiceException;
import com.project.poetry.common.util.CommonUtil;
import com.project.poetry.common.util.FileUtil;
import com.project.poetry.service.entity.dto.file.*;
import com.project.poetry.service.entity.po.file.FilePO;
import com.project.poetry.service.entity.po.file.SecretFilePO;
import com.project.poetry.service.entity.po.user.UserPO;
import com.project.poetry.service.mapper.FileMapper;
import com.project.poetry.service.mapper.SecretFileMapper;
import com.project.poetry.service.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
 * 文件业务层实现类
 * @author mengxj
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class FileServiceImpl implements FileService {

    @Value("${path.root-path}")
    private String rootPath;

    @Resource
    private FileMapper fileMapper;

    @Resource
    private SecretFileMapper secretFileMapper;

    @Override
    public void upload(FileUploadDTO fileUpload, UserPO user) {
        this.uploadFile(fileUpload, user, false);
    }


    private Long uploadFile(FileUploadDTO fileUpload, UserPO user, boolean flag) {
        MultipartFile file = fileUpload.getFile();
        String fileName = file.getOriginalFilename();
        assert fileName != null;
        String tempName = UUID.randomUUID().toString() + fileName.substring(fileName.lastIndexOf("."));
        tempName = FileUtil.getRealFileName(tempName);
        log.info("fileName: {} tempName: {}", fileName, tempName);
        String filePath = rootPath + tempName;
        this.saveFileByIo(file, filePath);
        FilePO po = new FilePO();
        po.setTempName(tempName);
        po.setFileName(fileName);
        po.setFilePath(filePath);
        String fileDesc = fileUpload.getFileDesc();
        po.setFileDesc(StrUtil.isBlank(fileDesc) ? fileName : fileDesc);
        po.setUserId(user.getId());
        po.setIsSecret(flag ? DictConstant.TRUE : DictConstant.FALSE);
        this.fileMapper.insert(po);
        return po.getId();
    }

    /**
     * 通过io流上传文件
     * @param file 文件对象
     * @param filePath 上传路径，形如：D://file/2021/09/10/a.txt
     */
    private void saveFileByIo(MultipartFile file, String filePath){
        FileUtil.checkAndCreateDir(filePath);
        try (InputStream is = file.getInputStream();
             FileOutputStream fos = new FileOutputStream(filePath)){
            int d;
            byte[] data = new byte[1024];
            while ((d = is.read(data)) != -1){
                fos.write(data, 0, d);
            }
        }catch (IOException e){
            e.printStackTrace();
            throw new ServiceException("上传文件失败！");
        }
    }

    @Override
    public IPage<FilePO> selectList(FileListDTO fileDto, UserPO user) {
        LambdaQueryWrapper<FilePO> wrapper = new QueryWrapper<FilePO>().lambda();
        wrapper.eq(FilePO::getIsSecret, DictConstant.FALSE);
        Object param;
        if(Objects.nonNull(param = fileDto.getRange()) && param.equals(DictConstant.RANGE_MY)){
            wrapper.eq(FilePO::getUserId, user.getId());
        }
        if(Objects.nonNull(param = fileDto.getFileName())){
            wrapper.like(FilePO::getFileName, param);
        }
        wrapper.orderByDesc(FilePO :: getUpdateTime);
        Page<FilePO> page = CommonUtil.getPage(fileDto.getPageNum(), fileDto.getPageSize());
        return this.fileMapper.selectPage(page, wrapper);
    }

    @Override
    public void download(Long id, HttpServletResponse response) {
        FilePO file = this.fileMapper.selectById(id);
        String fileName = file.getFileName();
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
        log.info("fileName: {}", fileName);
        try (FileInputStream fis = new FileInputStream(file.getFilePath());
             OutputStream os = response.getOutputStream()){
            byte[] data = new byte[1024];
            int len;
            while ((len = fis.read(data)) != -1){
                os.write(data, 0, len);
            }
            os.flush();
        }catch (IOException e){
            e.printStackTrace();
            throw new ServiceException("下载文件失败！");
        }
    }

    @Override
    public void delete(Long id, UserPO user) {
        FilePO file = this.fileMapper.selectById(id);
        if(!file.getUserId().equals(user.getId())){
            throw new ServiceException("该数据不是您上传的，您不能删除！");
        }
        fileMapper.deleteById(id);
        File f = new File(file.getFilePath());
        if(f.exists() && !f.delete()){
            throw new ServiceException("删除失败");
        }
    }

    @Override
    public void secretUpload(SecretFileUploadDTO secretFileUpload, UserPO user) {
        Long producer = user.getId();
        Long fileId = this.uploadFile(secretFileUpload, user, true);
        secretFileUpload.getConsumers().forEach(consumer -> {
            SecretFilePO secretFile = new SecretFilePO();
            secretFile.setConsumer(consumer);
            secretFile.setProducer(producer);
            secretFile.setFileId(fileId);
            secretFile.setIsDownload(DictConstant.FALSE);
            this.secretFileMapper.insert(secretFile);
        });
    }

    @Override
    public IPage<SecretFileListOutDTO> secretFileList(SecretFileListDTO secretFileUpload, UserPO user) {
        Long userId = user.getId();
        QueryWrapper<SecretFileListOutDTO> wrapper = new QueryWrapper<>();
        Integer send = secretFileUpload.getSend();
        wrapper.eq("tf.is_secret", DictConstant.TRUE);
        if(Objects.isNull(send) && !user.isVip()){
            wrapper.and(wrap -> wrap.eq("tcf.producer", userId).or().eq("tcf.consumer", userId));
        }
        String fileName = secretFileUpload.getFileName();
        CommonUtil.like(wrapper, "tf.file_name", fileName);
        wrapper.last(" order by tcf.is_download, tcf.update_time desc ");
        Integer pageNum = secretFileUpload.getPageNum(), pageSize = secretFileUpload.getPageSize();
        return this.secretFileMapper.secretFileList(CommonUtil.getPage(pageNum, pageSize), wrapper);
    }

    @Override
    public void deleteSecret(Long id, UserPO user) {
        SecretFilePO secretFile = this.secretFileMapper.selectById(id);
        Long fileId = secretFile.getFileId();
        if(secretFile.getProducer().equals(user.getId())){
            this.secretFileMapper.deleteById(id);
            this.delete(fileId, user);
        }else {
            List<SecretFilePO> secretList = this.secretFileMapper.selectList(
                    new QueryWrapper<SecretFilePO>().lambda().eq(SecretFilePO::getFileId, secretFile.getFileId()));
            if(CommonUtil.isEmpty(secretList) || secretList.size() == 1){
               // this.delete();
            }
        }
    }

    @Override
    public void downloadSecret(Long id, HttpServletResponse response) {
        SecretFilePO secretFile = this.secretFileMapper.selectById(id);
        this.download(secretFile.getFileId(), response);
    }
}
